#include "Framework.h"

#include "imgui_impl_win32.h"
#include "imgui_impl_dx12.h"
#include "imnodes.h"

LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Forward declare message handler from imgui_impl_win32.cpp
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Win32 message handler
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
        return true;

    return Bonnie::Framework::get()->MsgProc(hWnd, msg, wParam, lParam);
}

namespace Bonnie {
    Framework::Framework()
    {
        mIns = this;
    }

    LRESULT Framework::MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        switch (msg)
        {
        case WM_SIZE:
            if (Bonnie::Device::get()->hasInit() && wParam != SIZE_MINIMIZED)
            {
                Device::get()->flushCommandQueue();

                //mCmds[0]->mGraphCmdList->Reset(mCmds[0]->mCommandAllocator.Get(), nullptr);

                Device::get()->onResize(wParam, lParam);
                for (size_t i = 0; i < mCmds.size(); i++)
                {
                    mCmds[i]->onResize();
                }

                //mCmds[0]->mGraphCmdList->Close();
                //mCmds[9]->mQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)mCmds[0]->mGraphCmdList.GetAddressOf());
            }
            return 0;
        case WM_SYSCOMMAND:
            if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
                return 0;
            break;
        case WM_DESTROY:
            ::PostQuitMessage(0);
            return 0;
        }
        return ::DefWindowProc(hwnd, msg, wParam, lParam);
    }

    Framework* Framework::get()
    {
        return mIns;
    }

    Framework* Framework::mIns = nullptr;


    void Framework::init()
    {
        // Create application window
               //ImGui_ImplWin32_EnableDpiAwareness();
        WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL };
        ::RegisterClassEx(&wc);
        HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("Dear ImGui DirectX12 Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL);

        if (!Device::get()->initDevice(hwnd)) {
            ::UnregisterClass(wc.lpszClassName, wc.hInstance);
        }

        mMaxFrame = Device::get()->NUM_BACK_BUFFERS;
        D3D12_DESCRIPTOR_HEAP_DESC desc = {};
        desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
        desc.NumDescriptors = 1;
        desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
        if (Device::get()->mDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&mSrvDescHeap)) != S_OK)
            should_not_be_here();
        // Show the window
        ::ShowWindow(hwnd, SW_SHOWDEFAULT);
        ::UpdateWindow(hwnd);

        // Setup Dear ImGui context
        IMGUI_CHECKVERSION();
        ImGui::CreateContext();
        ImNodes::CreateContext();
        ImGuiIO& io = ImGui::GetIO(); (void)io;
        //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
        //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls

        // Setup Dear ImGui style
        ImGui::StyleColorsDark();
        //ImGui::StyleColorsClassic();

        // Setup Platform/Renderer backends
        ImGui_ImplWin32_Init(hwnd);
        ImGui_ImplDX12_Init(Device::get()->mDevice.Get(), mMaxFrame,
            DXGI_FORMAT_R8G8B8A8_UNORM, mSrvDescHeap.Get(),
            mSrvDescHeap->GetCPUDescriptorHandleForHeapStart(),
            mSrvDescHeap->GetGPUDescriptorHandleForHeapStart());

        for (size_t i = 0; i < mMaxFrame; i++)
        {
            mCmds.push_back(CommandList::create());
        }
    }

    void Framework::run()
    {
        init();
        // Main loop
        bool done = false;
        while (!done)
        {
            // Poll and handle messages (inputs, window resize, etc.)
            // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
            // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
            // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
            // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
            MSG msg;
            while (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
            {
                ::TranslateMessage(&msg);
                ::DispatchMessage(&msg);
                if (msg.message == WM_QUIT)
                    done = true;
            }
            if (done)
                break;

            // Start the Dear ImGui frame
            ImGui_ImplDX12_NewFrame();
            ImGui_ImplWin32_NewFrame();
            ImGui::NewFrame();


            mCmds[mCurrentFrame]->beginRecord();

            // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
            update(mCmds[mCurrentFrame]);

            // Rendering
            ImGui::Render();

            mCmds[mCurrentFrame]->setDefaultRTV();

            mCmds[mCurrentFrame]->mGraphCmdList->SetDescriptorHeaps(1, mSrvDescHeap.GetAddressOf());
            ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), mCmds[mCurrentFrame]->mGraphCmdList.Get());
            //renderGUI();

            auto fenceValue = mCmds[mCurrentFrame]->endRecord();

            Device::get()->present(fenceValue);

            mCurrentFrame++;
            mCurrentFrame %= mMaxFrame;
        }

        // Cleanup
        ImGui_ImplDX12_Shutdown();
        ImGui_ImplWin32_Shutdown();
        ImGui::DestroyContext();
        ImNodes::DestroyContext();
    }

}

